#define PrynInternal
#define PrynImplementation
#include <pryn.h>

#define PrynChangeBody(BODY) PrynBaseBody (change, { state = change->pState; BODY })
#define PrynChangeGetProperty(PropName, PropType, PropInitial, PropValue) PrynBaseGetProperty (, Change, change, PropName, PropType, PropInitial, { *output = PropValue; })
#define PrynChangeSetProperty(PropName, PropType, PropAssign) PrynBaseSetProperty (, Change, change, PropName, PropType, PropAssign)

PrynExport (PrynResult, prynChangeDispatchSimple, (PrynChangeType type, const PrynObject *object))
{
	PrynStartFunctionHasState ();
	PrynChange change;

	prynCall (prynChangeClear (&change, type, object));
	prynCall (prynChangeDispatch (&change));

	PrynEndFunction ();
}

PrynExport (PrynResult, prynChangeClear, (PrynChange *change, PrynChangeType type, const PrynObject *object))
{
	PrynStartFunction ();

	prynAssert (object, NullArgument);
	prynCall (prynObjectState (object, &state));
	prynAssert (type != PrynChangeTypeNone, BadArgument);

	prynClearObject (change);
	change->pState = state;
	change->pMonitor = 0;
	change->pObject = *object;
	change->pType = type;

	PrynEndFunction ();
}

PrynExport (PrynResult, prynChangeDispatch, (PrynChange *change))
{
	PrynChangeBody ({
		PrynObject object = change->pObject;

		while (1)
		{
			PrynMonitorList *monitors;
			PrynMonitor *monitor;

			prynObjectMonitors (&object, &monitors);
			for (monitor = monitors ? monitors->first : 0; monitor; monitor = monitor->pNext)
				if (PrynIsTrue (prynChangeTypeMatch (change->pType, monitor->pChanges)))
				{
					change->pMonitor = monitor;
					monitor->pNotifyFunction (change, monitor->pData);
				}

			if (prynObjectScope (&object, &object))
				break;
		}
	});
}

PrynChangeGetProperty (State, PrynState *, 0, change->pState)
PrynChangeGetProperty (Type, PrynChangeType, PrynChangeTypeNone, change->pType)
PrynChangeGetProperty (Monitor, PrynMonitor *, 0, change->pMonitor)
PrynChangeGetProperty (Object, PrynObject, nullObject, change->pObject)

PrynExport (PrynResult, prynChangeTypeMatch, (PrynChangeType type, PrynChangeType match))
{
	// Iteratively test type against match, making type more and more abstract. We're done if type is equal to match, or if type won't become more abstract.
	while (1)
	{
		PrynChangeType was;

		if (type == match) return PrynResultTrue;
		was = type;
		if (prynChangeTypeAbstract (type, &type)) return PrynResultFalse;
	}
}

PrynExport (PrynResult, prynChangeTypeAbstract, (PrynChangeType type, PrynChangeType *output))
{
	#define caseDone(NAME) case PrynChangeType##NAME: return PrynResultDone
	#define caseSimple(NAME, RESULT) case PrynChangeType##NAME: if (output) *output = PrynChangeType##RESULT; return PrynResultSuccess
	if (output) *output = PrynChangeTypeNone;

	switch (type)
	{
		caseDone (None);
		caseDone (All);
		caseSimple (Component, All);
		caseSimple (Pin, All);
		caseSimple (PinChangeValue, Pin);
	}
	return PrynResultUnknownEnumeration;
	#undef caseSimple
	#undef caseDone
}